/*===========================================

	MRS extractor

	Coders:
		TalkLittle

	Credits:
		Jongware

============================================*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include "common.h"

#pragma pack(push)
#pragma pack(1)

typedef struct _BITMAPFILEHEADERZ {
    uint16_t magic_number;
    uint32_t filesize;
    uint16_t reserved0;
    uint16_t reserved1;
    uint32_t offset;
} BITMAPFILEHEADERZ;

typedef struct _BITMAPINFOHEADERZ {
    uint32_t infoheadersize;
    int32_t width;
    int32_t height;
    uint16_t color_planes;
    uint16_t bitsperpixel;
    uint32_t compression_method;
    uint32_t imagesize;
    int32_t xresolution; // pixel per meter
    int32_t yresolution; // pixel per meter
    uint32_t ncpalette; // num colors in palette
    uint32_t nimportant; // num important colors
} BITMAPINFOHEADERZ;

#pragma pack(pop)

//static int bytes_read = 0, bytes_written = 0;
int endianness;

/* Find endianness of current machine. 0=Little Endian, 1=Big Endian */
int get_endianness() {
    char testme[2] = {0, 1};
    uint16_t num = 1;
    if (memcmp(testme, &num, 2) == 0)
        return 1;
    else
        return 0;
}

/* Reverse 2 bytes */
void bswap_16(void *p) {
    char b;
    char *ptr = (char *)p;
    b = ptr[0];
    ptr[0] = ptr[1];
    ptr[1] = b;
}
/* Reverse 4 bytes */
void bswap_32(void *p) {
    char b;
    char *ptr = (char *)p;
    b = ptr[0];
    ptr[0] = ptr[3];
    ptr[3] = b;
    b = ptr[1];
    ptr[1] = ptr[2];
    ptr[2] = b;
}

/* MRS stores BRG. BMP stores BGRA. */
/* @n: Number of colors total (assuming 3 bytes per color) */
void MRS_to_bmp_cmap(unsigned char *mrscmap, unsigned char *bmpcmap,
                     int nchannels, int colorsperchannel) {
    int i, bmpi;
    bmpi = 0;
    for (i = 0; i < 3 * nchannels * colorsperchannel; i += 3) {
        bmpcmap[bmpi++] = mrscmap[i+0]; // B
        bmpcmap[bmpi++] = mrscmap[i+2]; // G
        bmpcmap[bmpi++] = mrscmap[i+1]; // R
        bmpcmap[bmpi++] = 0;            // alpha
    }
}

/* Return BMP header to write to file. be sure to free() returned ptr */
unsigned char *MRS_to_bmp_header(MRSHEADER mrsheader, unsigned int bmpsize,
                                 int *outheadersize) {
    BITMAPFILEHEADERZ bmpfile; // 14 bytes
    BITMAPINFOHEADERZ bmpinfo; // 40 bytes
    unsigned char *returnme;
    int bmpcmaplen;
    uint32_t offset;

    bmpcmaplen = MRS_CMAP_LEN * 4 / 3; // BMP stores alpha
    offset = sizeof(BITMAPFILEHEADERZ) + sizeof(BITMAPINFOHEADERZ)
             + bmpcmaplen;

    memset(&bmpfile, 0, sizeof(BITMAPFILEHEADERZ));
    memcpy(&bmpfile.magic_number, "BM", 2);
    bmpfile.filesize = sizeof(BITMAPFILEHEADERZ) + sizeof(BITMAPINFOHEADERZ)
                       + bmpsize + bmpcmaplen;
    bmpfile.reserved0 = 0;
    bmpfile.reserved1 = 0;
    bmpfile.offset = offset;
    if (endianness == 1) {
        bswap_32(&bmpfile.filesize);
        bswap_32(&bmpfile.offset);
    }

    memset(&bmpinfo, 0, sizeof(BITMAPINFOHEADERZ));
    bmpinfo.infoheadersize = sizeof(BITMAPINFOHEADERZ);
    memcpy(&bmpinfo.width, &mrsheader[4], 2);
    memcpy(&bmpinfo.height, &mrsheader[6], 2);
    bmpinfo.color_planes = 1; // must be 1
    bmpinfo.bitsperpixel = 8;
    bmpinfo.compression_method = 0; // RGB
    bmpinfo.imagesize = bmpsize;
    bmpinfo.xresolution = 2835;
    bmpinfo.yresolution = 2835;
    bmpinfo.ncpalette = 256; /* XXX Need to figure out if all images use 16 or 256 color...*/
                             /*     If 16 at a time, then how to switch channels? */
    bmpinfo.nimportant = 0; // 0 == every color important
    if (endianness == 1) {
        bswap_32(&bmpinfo.infoheadersize);
        bswap_16(&bmpinfo.color_planes);
        bswap_16(&bmpinfo.bitsperpixel);
        bswap_32(&bmpinfo.compression_method);
        bswap_32(&bmpinfo.imagesize);
        bswap_32(&bmpinfo.xresolution);
        bswap_32(&bmpinfo.yresolution);
        bswap_32(&bmpinfo.ncpalette);
        bswap_32(&bmpinfo.nimportant);
    }

    returnme = (unsigned char *)malloc(offset); // headers + colormap
    memcpy(returnme, &bmpfile, 14);
    memcpy(&returnme[14], &bmpinfo, 40);

    /* The color map: MRS has 16 channels, 16 colors */
    MRS_to_bmp_cmap(&mrsheader[12], &returnme[54], 16, 16);

    *outheadersize = 54 + bmpcmaplen;

    return returnme;
}

static unsigned char fetch_byte(FILE *fp) {
    unsigned char b;
    //bytes_read += fread(&b, 1, 1, fp);
    fread(&b, 1, 1, fp);
    return b;
}

static void copy(FILE *fp, unsigned int copy_count, unsigned char *output) {
    unsigned char b;
    int i;
    for (i = 0; i < copy_count; i++) {
        //bytes_read += fread(&b, 1, 1, fp);
        fread(&b, 1, 1, fp);
        *(output++) = b;
    }
}

static void duplicate_px(unsigned char last_pixel,
                         unsigned int duplicate_count,
                         unsigned char *output) {
    int i;
    for (i = 0; i < duplicate_count; i++) {
        *(output++) = last_pixel;
    }
}

static void duplicate_multiple(unsigned char *source,
                               unsigned int duplicate_count,
                               unsigned char *output) {
    int i;
    /* Source and output may overlap, so copy 1 byte at a time */
    for (i = 0; i < duplicate_count; i++) {
        output[i] = source[i];
    }
}

/**
 * Wii specific
 */
void MRS_fillColorTable(const u8 *mrsPalette, u32 *colorTable, u8 alpha) {
	u8 i;
	u8 tmp[4];
	for (i = 0; i < 16; i++) {
		tmp[0] = mrsPalette[48*MRS_paletteNumber + 3*i + 1]; // R
		tmp[1] = mrsPalette[48*MRS_paletteNumber + 3*i + 2]; // G
		tmp[2] = mrsPalette[48*MRS_paletteNumber + 3*i + 0]; // B
		tmp[3] = alpha;                                      // A
		memcpy(&colorTable[i], tmp, 4);
	}
}

/**
 * Wii specific
 * Load MRS image into a buffer
 */
void MRS_ChangeFile(const char *mrsFile, u8 **outData, u32 *outColorTable,
		short *outWidth, short *outHeight) {
	MRSHEADER mrsheader;

	//Debug("MRS_ChangeFile: before MRS_extract_filename\n");
	*outData = MRS_extract_filename(MRSfp, mrsFile, outWidth, outHeight, mrsheader);
	//Debug("MRS_ChangeFile: before MRS_fillColorTable\n");
	MRS_fillColorTable(&mrsheader[MRS_PALETTE_OFFSET], outColorTable, 0xFF);
}

/**
 * Wii specific
 */
void MRS_Plot(u8 *data, u32 *colorTable, short xoff, short yoff,
		short width, short height) {
	u8 *cur;
	short x, y;

	//Debug("MRS_Plot: before x,y loop\n");
	cur = data;
	// Scanlines go from bottom to top
	for (y = 0; y < height; y++) {
		for (x = 0; x < width; x++) {
			GRRLIB_Plot(x + xoff, y + yoff, colorTable[*(cur++)]);
		}
	}
}

void MRS_extract_single(FILE *infp, int width, int height, unsigned char *output) {
    unsigned int x, y, copy_count, duplicate_count;
    unsigned char b;
    unsigned char *curr;
    unsigned char *source; // used in Option 3

    //fprintf(stderr, "DEBUG: Enter MRS_extract_single\n");

    curr = output;
    y = 0;
    do { // y
        //fprintf(stderr, "DEBUG: y is %d (x is %d)\n", y, x);
        x = 0;
        do { // x
            //fprintf(stderr, "DEBUG: x is %d\n", x);
            b = fetch_byte(infp);
            if ((b & 0xc0) == 0) {    // Option 1 (00xx.xxxx): Copy next pixel(s)
                //fprintf(stderr, "DEBUG: Option 1. Fetched byte is %d\n", b);
                if ((b & 0x3f) == 0)
                    copy_count = fetch_byte(infp) + 0x40;
                else
                    copy_count = b & 0x3f;
                /* Data that goes past the end of row gets truncated */
                if (copy_count > width - x) {
                    //fprintf(stderr, "WARN: Data exceeded row width by %d bytes\n", copy_count - (width - x));
                    copy_count = width - x;
                }
                copy(infp, copy_count, curr);
                curr += copy_count;
                x += copy_count;
            } else if ((b & 0x80) == 0) {   // Option 2 (01xx.xxxx): duplicate last pixel
                //fprintf(stderr, "DEBUG: Option 2. Fetched byte is %d\n", b);
                if ((b & 0x3f) == 0)
                    duplicate_count = fetch_byte(infp) + 0x41;
                else
                    duplicate_count = (b & 0x3f) + 1;
                /* Data that goes past the end of row gets truncated */
                if (duplicate_count > width - x) {
                    //fprintf(stderr, "WARN: Data exceeded row width by %d bytes\n", duplicate_count - (width - x));
                    duplicate_count = width - x;
                }
                duplicate_px(*(curr-1), duplicate_count, curr);
                curr += duplicate_count;
                x += duplicate_count;
            } else {    // Option 3 (1?xx.xxxx): duplicate existing pixels
                //fprintf(stderr, "DEBUG: Option 3. Fetched byte is %d\n", b);
                source = curr - ((b & 0x0f)*256 + fetch_byte(infp) + 1);
                duplicate_count = (b/16) & 7;
                if (duplicate_count == 0)
                    duplicate_count = fetch_byte(infp) + 10;
                else
                    duplicate_count = duplicate_count + 2;
                /* Data that goes past the end of row gets truncated */
                if (duplicate_count > width - x) {
                    //fprintf(stderr, "WARN: Data exceeded row width by %d bytes\n", duplicate_count - (width - x));
                    duplicate_count = width - x;
                }
                duplicate_multiple(source, duplicate_count, curr);
                curr += duplicate_count;
                x += duplicate_count;
            }

            if (feof(infp)) {
                //fprintf(stderr, "WARN: Premature end-of-file\n");
                return;
            }
        } while (x < width);
        y++;
    } while (y < height);
}

unsigned char *MRS_extract_filename(FILE *infp, const char *filename,
                                int16_t *width, int16_t *height,
                                MRSHEADER mrsheader) {
    unsigned char *output;
    int i;

    endianness = get_endianness();

    /* XXX make this faster? reorganize filenames into alphabetical order,
     * then do binary search over them? */
    for (i = 0; i < MRS_numFiles; i++) {
        if (!strcmp(filename, MRS_fileNames[i])) {
            fseek(infp, MRS_fileOffsets[i], SEEK_SET);
            //fprintf(stderr, "DEBUG: Seeked to %ld for file %s\n", MRS_fileOffsets[i], filename);
            //bytes_read += fread(mrsheader, 1, MRS_HEADER_LEN, infp);
            fread(mrsheader, 1, MRS_HEADER_LEN, infp);
            memcpy(width, &mrsheader[4], 2);
            memcpy(height, &mrsheader[6], 2);
            if (endianness == 1) {
                bswap_16(width);
                bswap_16(height);
            }
            output = (unsigned char *)malloc((*width) * (*height));

            MRS_extract_single(infp, *width, *height, output);

            return output;
        }
    }
    return NULL;
}

char *MRS_fileNames[] = {
    "SLIDE_2.MRS",
    "AG020A.MRS",
    "AG020M.MRS",
    "AG030.MRS",
    "AG030D.MRS",
    "AG030U.MRS",
    "AG040.MRS",
    "AG041.MRS",
    "AG050.MRS",
    "AG060.MRS",
    "AG070.MRS",
    "AG071.MRS",
    "AG080.MRS",
    "AG081.MRS",
    "AG082.MRS",
    "AG083A.MRS",
    "AG083M.MRS",
    "AG090M.MRS",
    "AG090MA.MRS",
    "AG090MP.MRS",
    "AG100M.MRS",
    "AG100MA.MRS",
    "B10A020.MRS",
    "B10A040.MRS",
    "B10A050.MRS",
    "B10A060.MRS",
    "B10A080.MRS",
    "BD010A.MRS",
    "BG010AA.MRS",
    "BG010M.MRS",
    "BG030A.MRS",
    "BG030M.MRS",
    "BG040.MRS",
    "BG050.MRS",
    "BG051.MRS",
    "BG060.MRS",
    "BG070.MRS",
    "BG080.MRS",
    "BG090.MRS",
    "BG100A.MRS",
    "BG100M.MRS",
    "BG101.MRS",
    "BG102.MRS",
    "BG103.MRS",
    "BG104.MRS",
    "BG110M.MRS",
    "BG110MA.MRS",
    "BG110MP.MRS",
    "BG120.MRS",
    "BJ_BG.MRS",
    "BLACK.MRS",
    "C10A030.MRS",
    "C10A040.MRS",
    "C10A050.MRS",
    "C10A060.MRS",
    "C10A080.MRS",
    "C10AAR.MRS",
    "C1AA012.MRS",
    "C1AA020.MRS",
    "C1AA080.MRS",
    "C1AA091.MRS",
    "CARENDA.MRS",
    "CDPLAY1.MRS",
    "CDPLAY2.MRS",
    "CG_R.MRS",
    "CG_R2.MRS",
    "CG010A.MRS",
    "CG010M.MRS",
    "CG030.MRS",
    "CG040.MRS",
    "CG050.MRS",
    "CG060.MRS",
    "CG080.MRS",
    "CG081.MRS",
    "CG082.MRS",
    "CG083.MRS",
    "CG084M.MRS",
    "CG084MA.MRS",
    "CG090A.MRS",
    "CG090AP.MRS",
    "CG090M.MRS",
    "CG100.MRS",
    "CGMODE.MRS",
    "CGMODE2.MRS",
    "CMK.MRS",
    "D1AA012.MRS",
    "D1AA020.MRS",
    "D1AA080.MRS",
    "D1AA100.MRS",
    "DG010A.MRS",
    "DG010AA.MRS",
    "DG010M.MRS",
    "DG011.MRS",
    "DG030.MRS",
    "DG031.MRS",
    "DG032A.MRS",
    "DG032M.MRS",
    "DG040A.MRS",
    "DG040M.MRS",
    "DG061.MRS",
    "DG070.MRS",
    "DG080.MRS",
    "DG090.MRS",
    "DG091.MRS",
    "DG092.MRS",
    "DG093.MRS",
    "DG094A.MRS",
    "DG094M.MRS",
    "DG100A.MRS",
    "DG110A.MRS",
    "DG110AP.MRS",
    "DG110M.MRS",
    "DG120.MRS",
    "E10A030.MRS",
    "E10A050.MRS",
    "E10A060.MRS",
    "E10A080.MRS",
    "E10A130.MRS",
    "E30A080.MRS",
    "EG010.MRS",
    "EG030.MRS",
    "EG040.MRS",
    "EG050.MRS",
    "EG050A.MRS",
    "EG060.MRS",
    "EG061.MRS",
    "EG061A.MRS",
    "EG062.MRS",
    "EG080.MRS",
    "EG080A.MRS",
    "EG090M.MRS",
    "EG090MA.MRS",
    "EG100.MRS",
    "EG100AA.MRS",
    "EG100AP.MRS",
    "EG110M.MRS",
    "EG110MA.MRS",
    "FG010.MRS",
    "FG010A.MRS",
    "FG020.MRS",
    "FG020A.MRS",
    "FG030.MRS",
    "FG040.MRS",
    "FG050.MRS",
    "FG060M.MRS",
    "FG060MA.MRS",
    "FG070.MRS",
    "FG080BA.MRS",
    "FG080BP.MRS",
    "FG080M.MRS",
    "FG090.MRS",
    "FG100M.MRS",
    "FG100MA.MRS",
    "FG110M.MRS",
    "FG110MA.MRS",
    "FG120.MRS",
    "G10A040.MRS",
    "G10A041.MRS",
    "G10A080.MRS",
    "G10A150.MRS",
    "G10A180.MRS",
    "G1A040L.MRS",
    "G1A040R.MRS",
    "GA010A.MRS",
    "GG010.MRS",
    "GG030.MRS",
    "GG040.MRS",
    "GG041.MRS",
    "GG050.MRS",
    "GG060.MRS",
    "GG070.MRS",
    "GG070A.MRS",
    "GG071.MRS",
    "GG072.MRS",
    "GG073.MRS",
    "GG074M.MRS",
    "GG074MA.MRS",
    "GG075MA.MRS",
    "GG080.MRS",
    "GG080AA.MRS",
    "GG080AP.MRS",
    "GG090.MRS",
    "GI010A.MRS",
    "HG010.MRS",
    "HG010A.MRS",
    "HG030.MRS",
    "HG040.MRS",
    "HG050.MRS",
    "HG051.MRS",
    "HG060.MRS",
    "HG061.MRS",
    "HG070.MRS",
    "HG080.MRS",
    "HG080A.MRS",
    "HG090.MRS",
    "HG110.MRS",
    "HG111.MRS",
    "HG120.MRS",
    "HG121.MRS",
    "HG122.MRS",
    "HG130.MRS",
    "HG130A.MRS",
    "HG131.MRS",
    "HG140MA.MRS",
    "HG140MB.MRS",
    "HG140MP.MRS",
    "HG140MXP.MRS",
    "HG150A.MRS",
    "IG010.MRS",
    "IG010A.MRS",
    "IG020.MRS",
    "IG030.MRS",
    "IG040D.MRS",
    "IG040U.MRS",
    "IG050.MRS",
    "IG060.MRS",
    "IG060A.MRS",
    "IG070.MRS",
    "IG080M.MRS",
    "IG090.MRS",
    "IG091.MRS",
    "IG100.MRS",
    "IG101.MRS",
    "IG101A5.MRS",
    "JG010.MRS",
    "KG010.MRS",
    "LG010.MRS",
    "LG010A.MRS",
    "LG020.MRS",
    "LG060A.MRS",
    "LOGO.MRS",
    "LOGO2.MRS",
    "MAIN.MRS",
    "MENU_G.MRS",
    "MG012.MRS",
    "MG020.MRS",
    "MG030.MRS",
    "MG040.MRS",
    "MG041.MRS",
    "MG050.MRS",
    "MG060.MRS",
    "MG070.MRS",
    "MG080.MRS",
    "MG091.MRS",
    "MG100.MRS",
    "MG110.MRS",
    "MG112.MRS",
    "MG130.MRS",
    "MG131.MRS",
    "MG133.MRS",
    "MG150.MRS",
    "MG180.MRS",
    "MOJI.MRS",
    "N_WIN.MRS",
    "NAME.MRS",
    "NAME2.MRS",
    "NAME3.MRS",
    "NAME4.MRS",
    "NG010.MRS",
    "NG020.MRS",
    "NG030.MRS",
    "NG040.MRS",
    "NG041.MRS",
    "NG050.MRS",
    "OTAKU.MRS",
    "PG010.MRS",
    "PG010A.MRS",
    "QG010.MRS",
    "QG010A.MRS",
    "QG010AC.MRS",
    "QG030.MRS",
    "QG040.MRS",
    "QG040A.MRS",
    "QG050M.MRS",
    "QG050MA.MRS",
    "QG060.MRS",
    "QG070.MRS",
    "QG071.MRS",
    "QG072.MRS",
    "QG073.MRS",
    "QG080.MRS",
    "SCAGUL.MRS",
    "SD_1.MRS",
    "SD_2.MRS",
    "SD_3.MRS",
    "SD_4.MRS",
    "SD_5.MRS",
    "SD_6.MRS",
    "SD_7.MRS",
    "SLIDE_1.MRS",
    "AC010A.MRS",
    "SLIDE_3.MRS",
    "STAFF.MRS",
    "TITLE.MRS",
    "TRUMP.MRS",
    "TRUMP2.MRS",
    "WIN.MRS",
    "WINDOW.MRS",
    "WINDOW2.MRS",
};

long MRS_fileOffsets[] = {
    4802,
    13656,
    16822,
    33916,
    47835,
    70454,
    104201,
    121868,
    139575,
    163411,
    188728,
    212258,
    237247,
    272367,
    306091,
    338555,
    340067,
    369719,
    404006,
    405227,
    409077,
    467520,
    469339,
    472033,
    474718,
    477310,
    479958,
    482664,
    487486,
    490327,
    514473,
    519041,
    551853,
    583817,
    591346,
    621029,
    651030,
    672146,
    706537,
    738423,
    742052,
    772213,
    803666,
    833795,
    863550,
    892698,
    922803,
    924463,
    927460,
    962704,
    967139,
    969522,
    973005,
    976484,
    979967,
    983464,
    986954,
    989807,
    992687,
    995542,
    998401,
    1001269,
    1010354,
    1022523,
    1025204,
    1033281,
    1070502,
    1075969,
    1099930,
    1139103,
    1157489,
    1182625,
    1207359,
    1238989,
    1266690,
    1293027,
    1318610,
    1342078,
    1343349,
    1345351,
    1350234,
    1380711,
    1424741,
    1434322,
    1471576,
    1472505,
    1475264,
    1478032,
    1480773,
    1483513,
    1488283,
    1493053,
    1520778,
    1535240,
    1550284,
    1564801,
    1566848,
    1579432,
    1583286,
    1602360,
    1628821,
    1653804,
    1677363,
    1700007,
    1721901,
    1742704,
    1763214,
    1764634,
    1783780,
    1812120,
    1813952,
    1818300,
    1846730,
    1889045,
    1891676,
    1894276,
    1896899,
    1899475,
    1902087,
    1904727,
    1919834,
    1931444,
    1955877,
    1983085,
    1984869,
    2019489,
    2053939,
    2088461,
    2123402,
    2151244,
    2154944,
    2180176,
    2184282,
    2227324,
    2229182,
    2232148,
    2270993,
    2272701,
    2284345,
    2286978,
    2299585,
    2302497,
    2330855,
    2345264,
    2371186,
    2403142,
    2408561,
    2434357,
    2436312,
    2455470,
    2488280,
    2514117,
    2552109,
    2556899,
    2585078,
    2586964,
    2623620,
    2626622,
    2629680,
    2632887,
    2636075,
    2639133,
    2642263,
    2645454,
    2651136,
    2668979,
    2697511,
    2722093,
    2745687,
    2769848,
    2796998,
    2824625,
    2829323,
    2859557,
    2889757,
    2919348,
    2947196,
    2954043,
    2960890,
    2994551,
    2996138,
    2998856,
    3039136,
    3043778,
    3060205,
    3062946,
    3091793,
    3129434,
    3160790,
    3192314,
    3219784,
    3245926,
    3271143,
    3296099,
    3298676,
    3325934,
    3348429,
    3371544,
    3406479,
    3441757,
    3476632,
    3499209,
    3501958,
    3526292,
    3530579,
    3591758,
    3601261,
    3612995,
    3651821,
    3662805,
    3665041,
    3692170,
    3718475,
    3733344,
    3762139,
    3790196,
    3825255,
    3828790,
    3851528,
    3877388,
    3923290,
    3934355,
    3955537,
    4005605,
    4061024,
    4085617,
    4097517,
    4108928,
    4111035,
    4127088,
    4130494,
    4149323,
    4164239,
    4172114,
    4179497,
    4196831,
    4216158,
    4240731,
    4252045,
    4257025,
    4275348,
    4302443,
    4319146,
    4353575,
    4371912,
    4395412,
    4409390,
    4424758,
    4447993,
    4470996,
    4502624,
    4522583,
    4528104,
    4532070,
    4533254,
    4542283,
    4554242,
    4560729,
    4567216,
    4579881,
    4592022,
    4604105,
    4617274,
    4653278,
    4664667,
    4731304,
    4760796,
    4763156,
    4779427,
    4782024,
    4784621,
    4817197,
    4839538,
    4843458,
    4864597,
    4866364,
    4894943,
    4916062,
    4926196,
    4941021,
    4960140,
    5021125,
    5024748,
    5044003,
    5070516,
    5113952,
    5143778,
    5171098,
    5198298,
    5220780,
    5229732,
    5235621,
    5243421,
    5245556,
    5259458,
    5269831,
    5293124,
    5294204,
    5308420,
    5320677,
};
int MRS_numFiles = sizeof(MRS_fileNames) / sizeof(*MRS_fileNames);

